{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Semantic Kernel Tool Use Example "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import the Needed Packages "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import asyncio\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "from typing import Annotated\n",
    "from openai import AsyncOpenAI\n",
    "\n",
    "\n",
    "from semantic_kernel.kernel import Kernel\n",
    "from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
    "from semantic_kernel.agents import ChatCompletionAgent\n",
    "from semantic_kernel.contents import ChatHistory\n",
    "\n",
    "\n",
    "from semantic_kernel.agents.open_ai import OpenAIAssistantAgent\n",
    "from semantic_kernel.contents import AuthorRole, ChatMessageContent\n",
    "from semantic_kernel.functions import kernel_function\n",
    "\n",
    "from semantic_kernel.connectors.ai import FunctionChoiceBehavior\n",
    "\n",
    "from semantic_kernel.contents.function_call_content import FunctionCallContent\n",
    "from semantic_kernel.contents.function_result_content import FunctionResultContent\n",
    "from semantic_kernel.functions import KernelArguments, kernel_function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the Plugins    \n",
    "Semantic Kernel uses plugins as tools that can be called by the agent. A plugin can have multiple `kernel_functions` in it as a group. \n",
    "\n",
    "In the example below, we create a `DestinationsPlugin` that has two functions: \n",
    "1. Provides a list of destinations using the `get_destinations` function\n",
    "2. Provides a lis of availablity for each destination using the `get_availabilty` function, "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define a sample plugin for the sample\n",
    "class DestinationsPlugin:\n",
    "    \"\"\"A List of Destinations for vacation.\"\"\"\n",
    "\n",
    "    @kernel_function(description=\"Provides a list of vacation destinations.\")\n",
    "    def get_destinations(self) -> Annotated[str, \"Returns the specials from the menu.\"]:\n",
    "        return \"\"\"\n",
    "        Barcelona, Spain\n",
    "        Paris, France\n",
    "        Berlin, Germany\n",
    "        \"\"\"\n",
    "\n",
    "    @kernel_function(description=\"Provides the availability of a destination.\")\n",
    "    def get_availability(\n",
    "        self, destination: Annotated[str, \"The destination to check availability for.\"]\n",
    "    ) -> Annotated[str, \"Returns the availability of the destination.\"]:\n",
    "        return \"\"\"\n",
    "        Barcelona - Unavailable\n",
    "        Paris - Available\n",
    "        Berlin - Available\n",
    "        \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "load_dotenv()\n",
    "client = AsyncOpenAI(\n",
    "    api_key=os.getenv(\"GITHUB_TOKEN\"), base_url=\"https://models.inference.ai.azure.com/\")\n",
    "\n",
    "kernel = Kernel()\n",
    "kernel.add_plugin(DestinationsPlugin(), plugin_name=\"destinations\")\n",
    "\n",
    "service_id = \"agent\"\n",
    "\n",
    "chat_completion_service = OpenAIChatCompletion(\n",
    "    ai_model_id=\"gpt-4o-mini\",\n",
    "    async_client=client,\n",
    "    service_id=service_id\n",
    ")\n",
    "kernel.add_service(chat_completion_service)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting the Function Choice Behavior \n",
    "\n",
    "In Semantic Kernel, we have the ability to have some control of the agent choice of functions. This is done by using the `FunctionChoiceBehavior` class. \n",
    "\n",
    "The code below sets it to `Auto` which allows the agent to choose among the available functions or not choose any. \n",
    "\n",
    "This can also be set to:\n",
    "`FunctionChoiceBehavior.Required` - to require the agent to choose at least one function \n",
    "`FunctionChoiceBehavior.NoneInvoke` - instructs the agent to not choose any function. (good for testing)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "settings = kernel.get_prompt_execution_settings_from_service_id(\n",
    "    service_id=service_id)\n",
    "settings.function_choice_behavior = FunctionChoiceBehavior.Auto()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the Agent \n",
    "Now we will create the Agent by using the Agent Name and Instructions that we can set. \n",
    "\n",
    "You can change these settings to see how the differences in the agent's response. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "AGENT_NAME = \"TravelAgent\"\n",
    "AGENT_INSTRUCTIONS = \"Answer questions about the travel destinations and their availability.\"\n",
    "# Create the agent\n",
    "agent = ChatCompletionAgent(\n",
    "    service_id=service_id,\n",
    "    kernel=kernel,\n",
    "    name=AGENT_NAME,\n",
    "    instructions=AGENT_INSTRUCTIONS,\n",
    "    arguments=KernelArguments(settings=settings),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running the Agent \n",
    "\n",
    "Now we wil run the AI Agent. In this snippet, we can add two messages to the `user_input` to show how the agent responds to followup questions. \n",
    "\n",
    "The agent should call the correct function to get the list of available destinations and confirm the availablity of a certain location. \n",
    "\n",
    "You can change the `user_inputs` to see how the agent responds. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# User: 'What destinations are available?'\n",
      "# Assistant - TravelAgent: 'The available travel destinations are:\n",
      "- Barcelona, Spain- Paris, France- Berlin, Germany'\n",
      "# User: 'Is Barcelona available?'\n",
      "# Assistant - TravelAgent: 'Barcelona is currently unavailable. However, Paris and Berlin are available.'\n"
     ]
    }
   ],
   "source": [
    "async def main():\n",
    "    # Define the chat history\n",
    "    chat_history = ChatHistory()\n",
    "\n",
    "    # Respond to user input\n",
    "    user_inputs = [\n",
    "        \"What destinations are available?\",\n",
    "        \"Is Barcelona available?\",\n",
    "    ]\n",
    "\n",
    "    for user_input in user_inputs:\n",
    "        # Add the user input to the chat history\n",
    "        chat_history.add_user_message(user_input)\n",
    "        print(f\"# User: '{user_input}'\")\n",
    "\n",
    "        agent_name: str | None = None\n",
    "        print(\"# Assistant - \", end=\"\")\n",
    "        async for content in agent.invoke_stream(chat_history):\n",
    "            if not agent_name:\n",
    "                agent_name = content.name\n",
    "                print(f\"{agent_name}: '\", end=\"\")\n",
    "            if (\n",
    "                not any(isinstance(item, (FunctionCallContent, FunctionResultContent))\n",
    "                        for item in content.items)\n",
    "                and content.content.strip()\n",
    "            ):\n",
    "                print(f\"{content.content}\", end=\"\", flush=True)\n",
    "        print(\"'\")\n",
    "\n",
    "await main()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
